package com.zhu.tool_test.starke.matrix.multipart.core;



import com.zhu.tool_test.response.GToolResopnse;
import com.zhu.tool_test.starke.constants.Messages;
import com.zhu.tool_test.starke.matrix.multipart.KeyConverter;
import com.zhu.tool_test.starke.matrix.multipart.annotations.MultipartKey;
import com.zhu.tool_test.starke.matrix.multipart.wrapper.DataWrapper;
import com.zhu.tool_test.starke.matrix.multipart.wrapper.WrapMapper;
import com.zhu.tool_test.starke.matrix.multipart.wrapper.WrapperStrategy;
import com.zhu.tool_test.starke.util.time.DateUtils;

import java.lang.reflect.Field;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.*;
import java.util.function.Predicate;

/**
 * 回调消息模型
 * 链式调用可以方便的设置回调的参数
 *
 * @author 4everlynn
 */
public class Data extends LinkedHashMap<String, Object> {
    private KeyConverter converter = new KeyConverter();
    private DataWrapper wrapper = new DataWrapper();




    // 转化成responseResult
    public GToolResopnse build(){
        GToolResopnse gToolResopnse = new GToolResopnse();
        // code msg success 设置
        gToolResopnse.setSuccess(this.success());
        gToolResopnse.setCode(this.getCode());
        this.remove("code");
        gToolResopnse.setMessage(this.getStringPart(com.zhu.tool_test.response.Messages.KEY_MSG));
        this.remove("msg");

        // 将Data里的对象 放入
       // Data data = new Data();

        gToolResopnse.setData(this);

        return gToolResopnse;

    }


    public Data strategy(WrapperStrategy strategy) {
        this.wrapper.setStrategy(strategy);
        return this;
    }

    protected Field[] getAllDeclaredFields(Class<?> clazz) {
        Class<?> superclass;
        List<Field> fieldList = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));
        while ((superclass = clazz.getSuperclass()) != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(superclass.getDeclaredFields())));
            clazz = superclass;
        }
        Field[] res = new Field[fieldList.size()];
        res = fieldList.toArray(res);
        return res;
    }

    /**
     * 添加参数
     *
     * @param key   键
     * @param value 目标值
     * @return 当前类
     */
    public Data include(String key, Object value) {
        super.put(key, value);
        return this;
    }

    public <T> T includeAndReturn(String key, T value) {
        super.put(key, value);
        return value;
    }

    /**
     * 返回前做一次包装
     *
     * @param mapper 映射器
     * @return 数据
     */
    public Data wrapper(WrapMapper mapper) {
        mapper.map(this.wrapper);
        wrapper.forEach((current, target) -> {
            // 如果指定了Scope
            String regex = ",";
            if (target.contains(regex)) {
                // 0 -> target key 1 -> scope
                String[] keys = target.split(regex);
                String targetKey = keys[0];
                String scope = keys[1];
                // 取得 Scope
                try {
                    this.getOptionalPart(scope
                            , MultipartData.class).ifPresent(res -> res.wrapper(wrapper -> {
                        wrapper.map(current, targetKey);
                    }));
                } catch (ClassCastException e) {
                    this.include(scope, new MultipartData().parties(this.get(scope)));
                    this.getOptionalPart(keys[1]
                            , MultipartData.class)
                            .ifPresent(res -> res.wrapper(wrapper -> wrapper.map(current, targetKey)));
                }
            } else {
                this.put(target, this.remove(current));
            }
        });
        return this;
    }

    /**
     * 复写get方法 将命名法转化后进行获取
     *
     * @param key 目标key 可以是下划线命名法或驼峰
     * @return 数据
     */
    public Object get(String key) {
        Object target = super.get(converter.convert(key));
        if (target != null) {
            return target;
        } else if ((target = super.get(converter.convertLine(key))) != null) {
            return target;
        } else {
            return super.get(key);
        }
    }

    /**
     * 获得数据并转化为对应类型
     *
     * @param key    目标key
     * @param target 目标类型
     * @param <T>    目标泛型
     * @return 数据
     */
    public <T> T getPart(String key, Class<T> target) {
        if (!isNull(key)) {
            Object o = get(key);
            if (o.getClass().equals(target) || target.isAssignableFrom(o.getClass())) {
                return (T) o;
            } else {
                if (target.isAssignableFrom(o.getClass())) {
                    return (T) o;
                } else {
                    classCanNotCastException(o.getClass(), target);
                }
            }
        }
        return null;
    }

    public MultipartData getPart(String key) {
        if (!isNull(key)) {
            Object o = get(key);
            if (Map.class.isAssignableFrom(o.getClass())) {
                Map map = (Map) o;
                MultipartData data = new MultipartData();
                for (Object k : map.keySet()) {
                    if (String.class.isAssignableFrom(k.getClass())) {
                        data.include(k.toString(), map.get(k));
                    }
                }
                return data;
            }
        }
        return null;
    }

    /**
     * 配合Java 8 Optional使用
     *
     * @param key    目标Key
     * @param target 目标类型
     * @param <T>    目标泛型
     * @return 目标
     */
    public <T> Optional<T> getOptionalPart(String key, Class<T> target) {
        return Optional.ofNullable(getPart(key, target));
    }


    /**
     * 从map中获得目标类型的数据
     *
     * @param tClass 类型
     * @param <T>    泛型
     * @return 数据
     */
    public <T> T get(Class<T> tClass) {
        Field[] declaredFields = getAllDeclaredFields(tClass);
        try {
            T t = tClass.newInstance();
            // declaredField 实体类中的属性
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                MultipartKey key = declaredField.getDeclaredAnnotation(MultipartKey.class);
                String targetKey = declaredField.getName();
                // 当前取的是MultipartKey情况下
                if (null != key && containsKey(key.value())) {
                    targetKey = key.value();
                }
                // val 需要被转换的值
                Object val = get(targetKey);
                if (val != null && declaredField.getType().equals(Long.class)) {
                    Long v = null;
                    if (Integer.class.isAssignableFrom(val.getClass())
                            || BigInteger.class.isAssignableFrom(val.getClass())) {
                        v = Long.parseLong(val.toString());
                    } else if (Long.class.isAssignableFrom(val.getClass())) {
                        v = (Long) val;
                    }
                    declaredField.set(t, v);
                } else if (val != null && declaredField.getType().equals(Float.class)) {
                    Float v = null;
                    if (Double.class.isAssignableFrom(val.getClass())
                            || Integer.class.isAssignableFrom(val.getClass())
                            || Long.class.isAssignableFrom(val.getClass())) {
                        v = Float.parseFloat(val.toString());
                        declaredField.set(t, v);
                    } else if (Float.class.isAssignableFrom(val.getClass())) {
                        v = (Float) val;
                    }
                    declaredField.set(t, v);

                } else if (val != null && declaredField.getType().equals(Double.class)) {
                    Double v = null;
                    if (Float.class.isAssignableFrom(val.getClass())
                            || Integer.class.isAssignableFrom(val.getClass())
                            || Long.class.isAssignableFrom(val.getClass())) {
                        v = Double.parseDouble(val.toString());
                        declaredField.set(t, v);
                    } else if (Double.class.isAssignableFrom(val.getClass())) {
                        v = (Double) val;
                    }
                    declaredField.set(t, v);
                } else if (val != null && declaredField.getType().equals(Date.class)) {
                    declaredField.set(t, getDatePart(declaredField.getName()));
                } else {
                    if (val != null && declaredField.getType().isAssignableFrom(val.getClass())) {
                        declaredField.set(t, val);
                    }
                }
                declaredField.setAccessible(false);
            }
            return t;
        } catch (InstantiationException | IllegalAccessException e) {
            return null;
        }
    }


    /**
     * @return 快捷获取相当于 get（"data"）
     */
    public Object getData() {
        return get(Messages.KEY_DATA);
    }

    public Integer getCode() {
        return getPart(Messages.KEY_CODE, Integer.class);
    }

    public Boolean codeSuccess() {
        return getCode().equals(Messages.CODE_SUCCESS);
    }

    /**
     * @return 快捷获取相当于 getPart("data", tClass);
     */
    public <T> T getData(Class<T> tClass) {
        return getPart(Messages.KEY_DATA, tClass);
    }

    /**
     * 判定目标key是否空
     *
     * @param key key
     * @return 是否空
     */
    private Boolean isNull(String key) {
        return this.get(key) == null;
    }

    /**
     * 获得数据并转化为String
     *
     * @param key key
     * @return 转化过后的值
     */
    public String getStringPart(String key) {
        String part = getPart(key, String.class);
        if (part == null) {
            return "";
        }
        return part;
    }

    public Optional<String> getStringOptionalPart(String key) {
        return Optional.ofNullable(this.getStringPart(key));
    }

    /**
     * 获得数据并转化为Float
     *
     * @param key
     * @return
     */
    public Float getFloatPart(String key) {
        return getPart(key, Float.class);
    }

    /**
     * 获得数据并转化为Boolean
     *
     * @param key key
     * @return 转化过后的值
     */
    public Boolean getBooleanPart(String key) {
        return getPart(key, Boolean.class);
    }

    /**
     * 获得数据并转化为Integer
     *
     * @param key key
     * @return 转化过后的值
     */
    public Integer getIntegerPart(String key) {
        return getPart(key, Integer.class);
    }

    /**
     * 获得数据并转化为 Long
     *
     * @param key key
     * @return 转化过后的值
     */
    public Long getLongPart(String key) {
        Object o = get(key);
        if (o == null) {
            return null;
        }
        if (Integer.class.isAssignableFrom(o.getClass())) {
            return Long.parseLong(o.toString());
        }
        return getPart(key, Long.class);
    }

    /**
     * 获得数据并转化为Integer
     *
     * @param key key
     * @return 转化过后的值
     */
    public Optional<Integer> getIntegerOptionalPart(String key) {
        return Optional.ofNullable(this.getIntegerPart(key));
    }

    /**
     * 获得数据并转化为Double
     *
     * @param key key
     * @return 转化过后的值
     */
    public Double getDoublePart(String key) {
        return getPart(key, Double.class);
    }

    /**
     * 获得数据并转化为Date
     *
     * @param key key
     * @return 转化过后的值
     */
    public Date getDatePart(String key) {
        Object o = get(key);
        if (o instanceof String) {
            return DateUtils.from(getStringPart(key));
        } else if (o instanceof Timestamp) {
            Timestamp timestamp = (Timestamp) o;
            return DateUtils.timestamp2Date(timestamp.getTime() / 1000);
        } else if (o instanceof Long) {
            Long ts = (Long) o;
            if (13 == ts.toString().length()) {
                return new Date(ts);
            } else if (10 == ts.toString().length()) {
                return new Date(ts * 10);
            } else {
                throw new RuntimeException("Unexpected time! key name is '" + key + "', value is '" + o + "'");
            }
        } else {
            return getPart(key, Date.class);
        }
    }

    private void classCanNotCastException(Class current, Class target) {
        throw new ClassCastException(current.getName() + " cant't cast to " + target.getName());
    }

    /**
     * 获得数据并转化为Map
     * 容易导致异常 慎用
     *
     * @param key key
     * @return 转化过后的值
     */
    @SuppressWarnings("unchecked")
    @Deprecated
    public <K, V> Map<K, V> getMapPart(String key, Class<K> keyType, Class<V> valueType) {
        Object o = get(key);
        if (o instanceof Map) {
            Map map = (Map) o;
            if (map.size() > 0) {
                if (map.keySet().toArray()[0].getClass().equals(keyType)) {
                    // 检查
                    map.forEach((k, v) -> {
                        if (!k.getClass().equals(keyType) && !keyType.isAssignableFrom(k.getClass())) {
                            classCanNotCastException(k.getClass(), keyType);
                        }
                        if (!v.getClass().equals(valueType) && !valueType.isAssignableFrom(v.getClass())) {
                            classCanNotCastException(v.getClass(), valueType);
                        }
                    });
                    return (Map<K, V>) o;
                }
            } else {
                return null;
            }
        } else {
            classCanNotCastException(o.getClass(), Map.class);
        }
        return new HashMap<>(0);
    }

    /**
     * 获得数据并转化为List
     *
     * @param key key
     * @return 转化过后的值
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> getListPart(String key, Class<T> tClass) {
        if (!isNull(key)) {
            Object o = get(key);
            if (o instanceof List) {
                List list = (List) o;
                if (list.size() > 0) {
                    Class<?> listType = list.get(0).getClass();
                    if (listType.equals(tClass)) {
                        return (List<T>) list;
                    }
                    if (Map.class.isAssignableFrom(listType)) {
                        List<MultipartData> temporary = new ArrayList<>();
                        list.forEach(item -> {
                            MultipartData data = new MultipartData();
                            Map map = (Map) item;
                            data.putAll(map);
                            temporary.add(data);
                        });
                        return MultipartData.trans2Type(temporary, tClass);
                    }
                    // 对 Long 类型做特殊处理
                    if (Long.class.equals(tClass)) {
                        List targetLongList = new ArrayList<>();
                        list.forEach(item -> targetLongList.add(Long.valueOf(item.toString())));
                        return targetLongList;
                    }
                }
                return list;
            } else {
                List list = new ArrayList();
                list.add(o);
                return list;
            }
        }
        // 避免空指针
        return new ArrayList<>();
    }

    private Field getFieldUpToRoot(Class<?> aClass, String currentKey) {
        Field[] allDeclaredFields = getAllDeclaredFields(aClass);
        for (Field allDeclaredField : allDeclaredFields) {
            if (allDeclaredField.getName().equals(currentKey)) {
                return allDeclaredField;
            }
        }
        return null;
    }


    public <T> Data includeIf(String key, T value, Predicate<T> predicate) {
        if (predicate.test(value)) {
            this.include(key, value);
        }
        return this;
    }

    /**
     * 获得数据并转化为Set
     *
     * @param key key
     * @return 转化过后的值
     */
    @SuppressWarnings("unchecked")
    public <T> Set<T> getSetPart(String key, Class<T> tClass) {
        if (!isNull(key)) {
            Object o = get(key);
            if (o instanceof Set) {
                Set list = (Set) o;
                if (list.size() > 0) {
                    if (list.toArray()[0].getClass().equals(tClass)) {
                        return (Set<T>) list;
                    }
                }
                return list;
            } else {
                Set list = new HashSet<>();
                list.add(o);
                return list;
            }
        }
        return new HashSet<>();
    }


    /**
     * 移除值
     *
     * @param key 键
     * @return 当前类
     */
    public Data exclude(String key) {
        super.remove(key);
        super.remove(converter.convert(key));
        super.remove(converter.convertLine(key));
        return this;
    }

    /**
     * 添加消息
     *
     * @param msg 消息内容
     * @return 当前类
     */
    public Data message(String msg) {
        this.include(Messages.KEY_MSG, msg);
        return this;
    }

    /**
     * 添加code
     *
     * @param code 代码
     * @return self
     */
    public Data code(Integer code) {
        this.include(Messages.KEY_CODE, code);
        return this;
    }

    public boolean success() {
        return this.getCode() == Messages.CODE_SUCCESS;
    }

    /**
     * 添加数据
     *
     * @param data 数据内容
     * @return 当前类
     */
    public Data data(Object data) {
        this.include(Messages.KEY_DATA, data);
        return this;
    }
}
